package org.scohen.juploadr.util;

import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.EncoderException;
import org.apache.commons.codec.net.URLCodec;

public class Persister<P> {
    protected static URLCodec codec = new URLCodec();
    private Class toPersist;
    private Preferences prefs;
    private String discriminatorMethod;
    
    private Persister(Class toPersist,String discriminatorMethod) {
        this.toPersist = toPersist;
        this.prefs = Preferences.userNodeForPackage(toPersist);
        this.discriminatorMethod = discriminatorMethod;
    }

    public static Persister<?> getInstance(Class toPersist) {
        return getInstance(toPersist,"name");
    }
    
    public static Persister<?> getInstance(Class toPersist, String discriminatorMethod) {
        return new Persister(toPersist,discriminatorMethod);
    }

    public void persist(P toPersist) {
        try {
            Map properties = BeanUtils.describe(toPersist);
            Iterator iter = properties.keySet().iterator();
            String encodedUsername = encode((String) properties.get(discriminatorMethod));

            while (iter.hasNext()) {

                String name = (String) iter.next();
                String propertyName = getShortClassName(toPersist.getClass()) + "." + encodedUsername + "." + name;
                String value = (String) properties.get(name);

                if (!"class".equals(name) && value != null && !value.startsWith("[")) {
                    prefs.put(propertyName, value.toString());
                }
            }
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    public void removeAll() {
        try {
            prefs.removeNode();
        } catch (BackingStoreException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    public void remove(P p){
        try {
            String[] keys = prefs.keys();
            String name = (String) BeanUtils.getProperty(p, discriminatorMethod);
            for(String key: keys) {
                String[] parts = key.split("\\.");
                if(name.equals(parts[1])){
                    prefs.remove(key);
                }
            }
        } catch (BackingStoreException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    public Collection<P> loadAll() {
        HashMap<String, P> usernames = new HashMap<String, P>();
        try {
            String[] keys = prefs.keys();
            for (int i = 0; i < keys.length; i++) {
                String[] keyParts = keys[i].split("\\.");
                if (getShortClassName(toPersist).equals(keyParts[0])) {
                    String decodedUsername = decode(keyParts[1]);
                    if (usernames.get(decodedUsername) == null) {
                        try {
                            usernames.put(decodedUsername, (P) toPersist.newInstance());
                        } catch (InstantiationException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                    P account = usernames.get(decodedUsername);
                    String property = prefs.get(keys[i], "");
                    if (property.startsWith("[")) {
                        // TODO set the indexed property

                    } else {
                        try {
                            BeanUtils.setProperty(account, keyParts[2], prefs.get(keys[i], ""));
                        } catch (IllegalAccessException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            }
        } catch (BackingStoreException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return usernames.values();
    }

    private String encode(String username) {
        String encoded = username;
        try {
            codec.encode(username);
        } catch (EncoderException ee) {
            // can't happen
        }
        encoded = encoded.replaceAll("\\.", "%2e");
        return encoded;
    }

    private static String decode(String encoded) {
        String decoded = "";
        try {
            decoded = codec.decode(encoded);
        } catch (DecoderException e) {
            // can't happen
        }
        return decoded;
    }

    private static String getShortClassName(Class clazz) {
        String className = clazz.getName();
        return className.substring(className.lastIndexOf(".") + 1);
    }
}
